﻿using System;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Documents;
using System.Windows.Ink;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Animation;
using System.Windows.Shapes;
using System.Collections.Generic;
using System.Windows.Markup;

namespace DiscoveryLogic.UI.Layouts.Controls
{
    public partial class FaveAspectContainer : UserControl
    {
        #region Private Variables


            // A list of references to aspects that this list owns
            private Dictionary<int, Aspect> aspectList = new Dictionary<int, Aspect>();

            // The minimum number of spots on a disc
            private int minSpots = 5;
            private int maxSpots = 7;

            // Item Count
            private int nextAspectIndex = 0;

            // List of Shadows
            private List<Rectangle> shadowList = new List<Rectangle>();

            // Is aspect container loaded
            private bool isAspectContainerLoaded = false;

            // Font sizes for aspects in this container
            private int detailTextSize = 8;
            private int titleTextSize = 9;

            // Variables used to calculate positions around disc
            private double containerLeft;
            private double containerTop;
            private double currentAngle;
            private double angleIncrement;
            private double itemHeight = 50;
            private double itemWidth = 50;
            private double containerWidth;
            private double containerHeight;
            private double radiusX;
            private double radiusY;
            private double centerX;
            private double centerY;
            private int itemCount;
            private double itemAngle;
            private double textAngle;
            private double x;
            private double y;
            private double textX;
            private double textY;

        #endregion

        #region Constructors

            public FaveAspectContainer()
            {
                InitializeComponent();                
            }

            public FaveAspectContainer(double canvasLeft, double canvasTop)
            {
                InitializeComponent();             

                // Set left and top properties
                LayoutRoot.SetValue(Canvas.LeftProperty, canvasLeft);
                LayoutRoot.SetValue(Canvas.TopProperty, canvasTop);

                // Initialize aspect slot shadows
                for (int i = 0; i < maxSpots; i++){
                    shadowList.Add(new Rectangle());
                    LayoutRoot.Children.Add(shadowList[i]);
                }

                this.Loaded += new RoutedEventHandler(FaveAspectContainer_Loaded);
            }

        #endregion

        #region Get/Set Properties

            private double currentScaleFactor = 1;
            // Gets/Sets the currentScaleFactor property of this FaveAspectContainer
            public double Scale
            {
                get{ return this.currentScaleFactor; }
                set{
                   
                    this.currentScaleFactor = value;

                    // If loaded, then find animation, set values and start, 
                    // otherwise just set value
                    if (isAspectContainerLoaded){
                    
                        DiscResizeKeyFrameX.Value = value;
                        DiscResizeKeyFrameY.Value = value;

                        DiscResize.Begin();
                    }
                    else{

                        double _width = LayoutRoot.Width;
                        double _height = LayoutRoot.Height;
                        double _centerX = _width / 2;
                        double _centerY = _height / 2;

                        // Find scale transform and update it
                        ScaleFaveAspectContainer.ScaleX = value;
                        ScaleFaveAspectContainer.ScaleY = value;
                        ScaleFaveAspectContainer.CenterX = _centerX;
                        ScaleFaveAspectContainer.CenterY = _centerY;
                    }                    
                }

            }

        #endregion

        #region Public Methods

            // Called by View360 content screen
            // Used to add aspect to nearest free spot
            public bool AddToNearest(Aspect a)
            {
                // Check if there is room and aspect is not
                // being from same list
                if (aspectList.Count >= maxSpots && a.Owner != this)
                {
                    return false;
                }

                // Set variables used in calculation of position around disc
                double aspectLeft = a.Left;
                double aspectTop = a.Top;

                containerLeft = Convert.ToDouble(LayoutRoot.GetValue(Canvas.LeftProperty));
                containerTop = Convert.ToDouble(LayoutRoot.GetValue(Canvas.TopProperty));

                currentAngle = 0;
                angleIncrement = 1.5;

                containerWidth = LayoutRoot.Width;
                containerHeight = LayoutRoot.Height;

                radiusX = (containerWidth * currentScaleFactor) / 2 - itemWidth;
                radiusY = (containerHeight * currentScaleFactor) / 2 - itemHeight / 2;
                centerX = containerWidth / 2 - itemWidth / 2;
                centerY = containerHeight / 2 - itemHeight / .98;

                currentAngle += angleIncrement;

                // If item count is below min spots, then set it to min spots
                // to ensure proper spacing
                int itemCount = aspectList.Count;

                if (itemCount < minSpots)
                {
                    itemCount = minSpots;
                }

                // Find free spot, set lowestDistance to arbitrary high number
                int freeSpot = 0;
                double lowestDistance = 1000;
                double freeSpotDistance;

                // For each spot, calculate x,y position
                for (int i = 0; i < itemCount; i++)
                {
                    // Calculate x,y position of slot
                    double itemAngle = 2 * Math.PI * i / itemCount + currentAngle;
                    double x = radiusX / 1.1 * Math.Cos(itemAngle) + centerX;
                    double y = radiusY * Math.Sin(itemAngle) + centerY;

                    // Offset due to container positioning
                    x += containerLeft;
                    y += containerTop;

                    // Calculate distance and see if this is the shortest dist so far
                    freeSpotDistance = Math.Sqrt(Math.Pow(a.Left - x, 2) + Math.Pow(a.Top - y, 2));

                    if (freeSpotDistance < lowestDistance)
                    {
                        lowestDistance = freeSpotDistance;
                        freeSpot = i;
                    }                    
                }

                RemoveAddAspect(a, freeSpot);

                return true;
            }

            // Used to add an aspect to this container
            public void AddAspect(Aspect a, int pos)
            {                
                // Check if another aspect occupies this position
                if (aspectList.ContainsKey(pos))
                {
                    // Assume we have to create a new spot.
                    // Then search to see if there's a free spot
                    int availSpot = nextAspectIndex;                
                    nextAspectIndex++;
                    
                    int i = (pos + 1) % (nextAspectIndex-1);
                    
                    // Find first free spot
                    while(i != pos)
                    {
                        if (aspectList.ContainsKey(i) == false){
                            availSpot = i;
                            nextAspectIndex--;
                            break;
                        }

                        i = (i + 1) % (nextAspectIndex-1);
                    }
                    
                    // Shift items to make room for new aspect                    
                    while(availSpot != pos)
                    {
                        int priorSpot = ((availSpot - 1) + (nextAspectIndex)) % (nextAspectIndex);
                        Aspect a2 = aspectList[priorSpot];
                        aspectList.Remove(priorSpot);
                        aspectList.Add(availSpot, a2);

                        availSpot = priorSpot;
                    }                
                }            

                // Add the aspect to the list
                aspectList.Add(pos, a);

                // Check if we have to grow the disc
                if (aspectList.Count > minSpots){
                    GrowDisc();
                }

                // Set owner property of aspect
                a.Owner = this;
                a.isFavourite = true;

                // Set font size and opacity of text details
                a.DetailText2Opacity = 100;
                a.DetailText3Opacity = 100;                
                a.DetailTextSize = detailTextSize;
                a.TitleTextSize = titleTextSize;

                // Up nextAspectIndex point
                if (pos >= nextAspectIndex){
                    nextAspectIndex = pos + 1;
                }

                // Redistribute them in the ring
                DistributeAspects();
            }

            // Used to remove an aspect by its key
            public void RemoveAspect(int key)
            {
                // Set owner and isFave properties of aspect
                // and remove it from aspectList
                aspectList[key].Owner = null;
                aspectList[key].isFavourite = false;
                aspectList.Remove(key);

                // If we have more items than minSpots,
                // close the gap in aspectList
                if (aspectList.Count >= minSpots)
                {
                    // Fill empty spot and shift items accordingly
                    for(int i = key; i < aspectList.Count; i++)
                    {                    
                        Aspect a2 = aspectList[i+1];
                        aspectList.Remove(i+1);
                        aspectList.Add(i, a2);
                    }

                    nextAspectIndex = aspectList.Count;

                    // Shrink the disc
                    ShrinkDisc();
                }
            }

            // Used to remove an aspect from aspectList given the aspect
            public void RemoveAspect(Aspect a)
            {
                // Cycle through dictionary, look for the item and remove it
                foreach (KeyValuePair<int, Aspect> discItem in aspectList)
                {
                    // If aspect exists, remove it
                    if (discItem.Value.Equals(a)){
                        a.Owner = null;
                        a.isFavourite = false;
                        aspectList.Remove(discItem.Key);

                        // If we have more aspects than minSpots,
                        // fill the gap in aspectList
                        if (aspectList.Count >= minSpots){
                            int key = discItem.Key;

                            // Fill empty spot and shift items accordingly
                            for (int i = key; i < aspectList.Count; i++)
                            {
                                Aspect a2 = aspectList[i + 1];
                                aspectList.Remove(i + 1);
                                aspectList.Add(i, a2);
                            }

                            nextAspectIndex = aspectList.Count;

                            // Shrink Disc
                            ShrinkDisc();
                        }

                        // We found the aspect, exit
                        break;
                    }
                }
            }

            public void RemoveAddAspect(Aspect a, int pos)
            {
                // Remove from old list
                if (!(a.Owner == null))
                {
                    if (a.Owner.GetType() == typeof(FaveAspectContainer))
                    {
                        ((FaveAspectContainer)a.Owner).RemoveAspect(a);
                    }
                    //else if (a.Owner.GetType() == typeof(CrescentAspectContainer))
                   // {
                    //    ((CrescentAspectContainer)a.Owner).RemoveAspect(a);
                   // }
                }

                // Add to disc
                AddAspect(a, pos);
            }  

            // Used to distribute the aspects on the ring
            public void DistributeAspects()
            {
                // Set variables used in calculation of position around disc
                containerLeft = Convert.ToDouble(LayoutRoot.GetValue(Canvas.LeftProperty));
                containerTop = Convert.ToDouble(LayoutRoot.GetValue(Canvas.TopProperty));

                currentAngle = 0;
                angleIncrement = 1.5;

                containerWidth = LayoutRoot.Width;
                containerHeight = LayoutRoot.Height;

                radiusX = (containerWidth * currentScaleFactor) / 2 - itemWidth;
                radiusY = (containerHeight * currentScaleFactor) / 2 - itemHeight / 2;
                centerX = containerWidth / 2 - itemWidth / 2;
                centerY = containerHeight / 2 - itemHeight / .98;
                double textDistanceFactorX = 1;
                double textDistanceFactorY = 1;

                currentAngle += angleIncrement;

                // If item count is below min spots, then set it to min spots
                // to ensure proper spacing
                itemCount = aspectList.Count;

                if (itemCount <= minSpots){
                    itemCount = minSpots;
                }
                else{
                    // More than min spots mean we have
                    // to pull items closer
                    textDistanceFactorX = 1.2;
                    textDistanceFactorY = 1.6;
                }

                // For each item, calculate position
                for (int i = 0; i < itemCount; i++)
                {
                    // Calculate x,y position of aspect
                    itemAngle = 2 * Math.PI * i / itemCount + currentAngle;
                    textAngle = 2 * Math.PI * i / itemCount;
                    x = radiusX / 1.1 * Math.Cos(itemAngle) + centerX;
                    y = radiusY / 1.1 * Math.Sin(itemAngle) + centerY;

                    // Calculate x,y position of aspect details text
                    textX = radiusX /1.35 * Math.Cos(itemAngle)/textDistanceFactorX;
                    textY = radiusY / 0.8 * Math.Sin(itemAngle)/textDistanceFactorY;

                    // If aspect exists in this spot
                    if (aspectList.ContainsKey(i))
                    {                        
                        // Set properties and position
                        aspectList[i].Left = x + containerLeft;
                        aspectList[i].Top = y + containerTop;
                        aspectList[i].ZOrder = Convert.ToInt32(Math.Abs(y));
                        aspectList[i].SetDetailsTextPosition(textX - 25, textY + 20);
                    }
                }
            }

            // Used to test if an aspect overlaps with any slots on the disc
            public bool HitTest(Aspect a)
            {
                // Check if there is room and aspect is not
                // being from same list
                if (aspectList.Count >= maxSpots && a.Owner != this){
                    return false;
                }

                // Set variables used in calculation of position around disc
                double aspectLeft = a.Left;
                double aspectTop = a.Top;

                containerLeft = Convert.ToDouble(LayoutRoot.GetValue(Canvas.LeftProperty));
                containerTop = Convert.ToDouble(LayoutRoot.GetValue(Canvas.TopProperty));

                currentAngle = 0;
                angleIncrement = 1.5;

                containerWidth = LayoutRoot.Width;
                containerHeight = LayoutRoot.Height;

                radiusX = (containerWidth * currentScaleFactor) / 2 - itemWidth;
                radiusY = (containerHeight * currentScaleFactor) / 2 - itemHeight / 2;
                centerX = containerWidth / 2 - itemWidth / 2;
                centerY = containerHeight / 2 - itemHeight / .98;

                currentAngle += angleIncrement;

                // If item count is below min spots, then set it to min spots
                // to ensure proper spacing
                int itemCount = aspectList.Count;

                if (itemCount < minSpots){
                    itemCount = minSpots;
                }

                // For each spot, calculate x,y position
                for (int i = 0; i < itemCount; i++)
                {
                    // Calculate x,y position of slot
                    double itemAngle = 2 * Math.PI * i / itemCount + currentAngle;
                    double x = radiusX / 1.1 * Math.Cos(itemAngle) + centerX;
                    double y = radiusY * Math.Sin(itemAngle) + centerY;

                    // Offset due to container positioning
                    x += containerLeft;
                    y += containerTop;

                    // If aspect lies in vicinity of this slot
                    if (aspectLeft > x - (92 * currentScaleFactor) &&
                        aspectLeft < x + (92 * currentScaleFactor) &&
                        aspectTop > y - (42 * currentScaleFactor) &&
                        aspectTop < y + (42 * currentScaleFactor))
                    {
                        // Remove aspect from old owner
                        // and add to this container
                        RemoveAddAspect(a, i);
                        return true;
                    }
                }
                // Aspect is not overlapping with any slots
                if (aspectLeft + 50 > containerLeft &&
                    aspectLeft < containerLeft + containerWidth &&
                    aspectTop + 50 > containerTop &&
                    aspectTop < containerTop + containerHeight && 
                    a.Owner != this)
                {
                    // Remove aspect from old owner
                    // and add to this container
                    AddToNearest(a);
                    return true;
                }
                else
                {
                    return false;
                }
            }

            // Used to show possible spots to drag aspect onto
            public void ShowSpots(UserControl aspectOwner)
            {
                // Check if there is room and aspect is not
                // being from same list
                if (aspectList.Count >= maxSpots && aspectOwner != this){
                    return;
                }

                // Set variables used in calculation of position around disc
                containerLeft = Convert.ToDouble(LayoutRoot.GetValue(Canvas.LeftProperty));
                containerTop = Convert.ToDouble(LayoutRoot.GetValue(Canvas.TopProperty));

                currentAngle = 0;
                angleIncrement = 1.5;

                containerWidth = LayoutRoot.Width;
                containerHeight = LayoutRoot.Height;

                radiusX = (containerWidth * currentScaleFactor) / 2 - itemWidth;
                radiusY = (containerHeight * currentScaleFactor) / 2 - itemHeight / 2;
                centerX = containerWidth / 2 - itemWidth / 2;
                centerY = containerHeight / 2 - itemHeight / .98;

                currentAngle += angleIncrement;

                // If item count is below min spots, then set it to min spots
                // to ensure proper spacing
                itemCount = aspectList.Count;

                if (itemCount <= minSpots){
                    itemCount = minSpots;
                }

                // For each slot
                for (int i = 0; i < itemCount; i++)
                {
                    // Calculate x,y position
                    itemAngle = 2 * Math.PI * i / itemCount + currentAngle;
                    textAngle = 2 * Math.PI * i / itemCount;
                    x = radiusX / 0.8 * Math.Cos(itemAngle) + centerX;
                    y = radiusY /0.8 * Math.Sin(itemAngle) + centerY;

                    // Add container offset
                    x -= 80;
                    y += 40;

                    // Set shadow properties
                    shadowList[i].SetValue(Canvas.LeftProperty, x);
                    shadowList[i].SetValue(Canvas.TopProperty, y);
                    shadowList[i].Width = 200;
                    shadowList[i].Height = 60;
                    shadowList[i].Opacity = 1;

                    // Add brush to fill property of shadow
                    RadialGradientBrush rgb = new RadialGradientBrush();
                    GradientStop gs1 = new GradientStop();
                    GradientStop gs2 = new GradientStop();
                    GradientStop gs3 = new GradientStop();

                    gs1.Color = Utils.HexToColor("#3F000000");
                    gs1.Offset = 0.202;
                    gs2.Color = Utils.HexToColor("#00000000");
                    gs2.Offset = 1;
                    gs2.Color = Colors.Transparent;
                    gs3.Offset = 0.549;

                    rgb.GradientStops.Add(gs1);
                    rgb.GradientStops.Add(gs2);
                    rgb.GradientStops.Add(gs3);

                    shadowList[i].Fill = rgb;
                }
            }

            // Used to hide slot shadows
            public void HideSpots()
            {
                // For each shadow
                for (int i = 0; i < shadowList.Count; i++)
                {
                    // Set opacity to 0
                    shadowList[i].Opacity = 0;
                }
            }

        #endregion

        #region Private Methods

            // Called when aspect is added and itemcount
            // is > minItems
            // Used to grow disc by 10%
            private void GrowDisc()
            {
                Scale = this.Scale + 0.10;
            }

            // Called when aspect is remove and itemcount
            // is still > minItems
            // Used to shrink disc by 10%
            private void ShrinkDisc()
            {
                Scale = this.Scale - 0.10;
                
                // Reposition aspects
                DistributeAspects();
            }

            // Called when canvas is loaded
            // Used to set isAspectContainerLoaded flag
            private void FaveAspectContainer_Loaded(object sender, EventArgs e)
            {
                isAspectContainerLoaded = true;
            }

        #endregion 

    }
}
